package com.netx.generics.sql;
import java.text.DateFormat;
import java.util.NoSuchElementException;
import java.sql.SQLException;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.LinkedList;
import java.util.Iterator;

import com.netx.generics.basic.Checker;
import com.netx.generics.time.Date;
import com.netx.generics.time.Moment;
import com.netx.generics.time.Time;
import com.netx.generics.util.Strings;


public class Table {

	private final String[] _columns;
	private final String[][] _rows;

	public Table(ResultSet rs) throws SQLException {
		ResultSetMetaData meta = rs.getMetaData();
		_columns = new String[meta.getColumnCount()];
		for(int i=0; i<_columns.length; i++) {
			_columns[i] = meta.getColumnName(i+1);
		}
		LinkedList<String[]> buffer = new LinkedList<String[]>();
		while(rs.next()) {
			String[] line = new String[_columns.length];
			for(int i=0; i<_columns.length; i++) {
				line[i] = rs.getString(i+1);
			}
			buffer.add(line);
		}
		_rows = new String[buffer.size()][];
		Iterator<String[]> it = buffer.iterator();
		for(int i=0; it.hasNext(); i++) {
			_rows[i] = it.next();
		}
	}

	public int getColumnCount() {
		return _columns.length;
	}

	public int getRowCount() {
		return _rows.length;
	}
	
	public boolean isEmpty() {
		return getRowCount()==0;
	}

	public int getColumnIndex(String colName) {
		Checker.checkEmpty(colName, "colName");
		for(int i=0; i<_columns.length; i++) {
			if(_columns[i].equals(colName)) {
				return i+1;
			}
		}
		return -1;
	}
	
	public String getColumnName(int index) {
		_checkColumnIndex(index);
		return _columns[index-1];
	}

	public Row getRow(int index) {
		_checkRowIndex(index);
		return new Row(this, index);
	}

	public Column getColumn(int index) {
		_checkColumnIndex(index);
		return new Column(this, index);
	}

	public Column getColumn(String colName) {
		int index = getColumnIndex(colName);
		if(index == -1) {
			return null;
		}
		else {
			return new Column(this, index);
		}
	}
	
	public String getString(int column, int row) {
		String v = _get(column, row);
		return Strings.isEmpty(v) ? null : v;
	}

	public String getString(String column, int row) {
		return getString(_getColumnIndex(column), row);
	}
	
	public boolean getBoolean(int column, int row) {
		return getInt(column, row) != 0;
	}

	public boolean getBoolean(String column, int row) {
		return getBoolean(_getColumnIndex(column), row);
	}

	public char getChar(int column, int row) {
		String v = _get(column, row);
		return Strings.isEmpty(v) ? '\0' : v.charAt(0);
	}

	public char getChar(String column, int row) {
		return getChar(_getColumnIndex(column), row);
	}

	public byte getByte(int column, int row) {
		String v = _get(column, row);
		return Strings.isEmpty(v) ? 0 : new Byte(v).byteValue();
	}

	public byte getByte(String column, int row) {
		return getByte(_getColumnIndex(column), row);
	}

	public short getShort(int column, int row) {
		String v = _get(column, row);
		return Strings.isEmpty(v) ? 0 : new Short(v).shortValue();
	}

	public short getShort(String column, int row) {
		return getShort(_getColumnIndex(column), row);
	}

	public int getInt(int column, int row) {
		String v = _get(column, row);
		return Strings.isEmpty(v) ? 0 : new Integer(v).intValue();
	}

	public int getInt(String column, int row) {
		return getInt(_getColumnIndex(column), row);
	}

	public long getLong(int column, int row) {
		String v = _get(column, row);
		return Strings.isEmpty(v) ? 0 : new Long(v).longValue();
	}

	public long getLong(String column, int row) {
		return getLong(_getColumnIndex(column), row);
	}

	public float getFloat(int column, int row) {
		String v = _get(column, row);
		return Strings.isEmpty(v) ? 0.0F : new Float(v).floatValue();
	}

	public float getFloat(String column, int row) {
		return getFloat(_getColumnIndex(column), row);
	}

	public double getDouble(int column, int row) {
		String v = _get(column, row);
		return Strings.isEmpty(v) ? 0.0F : new Double(v).doubleValue();
	}

	public double getDouble(String column, int row) {
		return getDouble(_getColumnIndex(column), row);
	}

	public Moment getMoment(int column, int row, DateFormat df) {
		if(df == null) {
			df = DateFormat.getDateTimeInstance();
		}
		String v = _get(column, row);
		return Strings.isEmpty(v) ? null : Moment.valueOf(v, df);
	}

	public Moment getMoment(int column, int row) {
		return getMoment(column, row, null);
	}

	public Moment getMoment(String column, int row, DateFormat df) {
		return getMoment(_getColumnIndex(column), row, df);
	}

	public Moment getMoment(String column, int row) {
		return getMoment(_getColumnIndex(column), row);
	}

	public Date getDate(int column, int row, DateFormat df) {
		if(df == null) {
			df = DateFormat.getDateInstance();
		}
		String v = _get(column, row);
		return Strings.isEmpty(v) ? null : Date.valueOf(v, df);
	}

	public Date getDate(int column, int row) {
		return getDate(column, row, null);
	}

	public Date getDate(String column, int row, DateFormat df) {
		return getDate(_getColumnIndex(column), row, df);
	}

	public Date getDate(String column, int row) {
		return getDate(_getColumnIndex(column), row);
	}

	public Time getTime(int column, int row, DateFormat df) {
		if(df == null) {
			df = DateFormat.getTimeInstance();
		}
		String v = _get(column, row);
		return Strings.isEmpty(v) ? null : Time.valueOf(v, df);
	}

	public Time getTime(int column, int row) {
		return getTime(column, row, null);
	}

	public Time getTime(String column, int row, DateFormat df) {
		return getTime(_getColumnIndex(column), row, df);
	}

	public Time getTime(String column, int row) {
		return getTime(_getColumnIndex(column), row);
	}

	private int _getColumnIndex(String colname) {
		int index = getColumnIndex(colname);
		if(index == -1) {
			throw new NoSuchElementException(colname);
		}
		else {
			return index;
		}
	}

	private String _get(int column, int row) {
		_checkRowIndex(row);
		_checkColumnIndex(column);
		return _rows[row][column-1];
	}
	
	private void _checkColumnIndex(int column) {
		if(column<1 || column>_columns.length) {
			throw new IndexOutOfBoundsException(column+"");
		}
	}
	
	private void _checkRowIndex(int row) {
		if(row<0 || row>=_rows.length) {
			throw new IndexOutOfBoundsException("length:"+_rows.length+" index:"+row);
		}
	}
}
